import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import lejos.nxt.ADSensorPort;
import lejos.nxt.Button;
import lejos.nxt.LCD;
import lejos.nxt.LightSensor;
import lejos.nxt.SensorPort;
import lejos.nxt.Sound;

/**
 * Extends the LightSensor class by adding functionality to calibrate the
 * light sensor. The calibration data can be stored in and retrieved from
 * a *.dat file called 'calib.dat'.
 *
 * @author Christoph Ammann
 * @see LightSensor
 */
public class ACalibratedLS extends LightSensor {

	/* Store the black value (equals low value) */
	private int black;
	/* Store the silver value (equals high value) */
	private int silver;
	/* Stores the name of the data file */
	private static final String FILE = "calib.dat";
	/* Stores the indentation of each plotted line */
	private static final int HSPACE = 2;

	/**
	 * Calls the constructor of the super class.
	 *
	 * @see LightSensor#LightSensor(ADSensorPort, boolean)
	 */
	public ACalibratedLS(ADSensorPort port, boolean floodlight) {
		super(port, floodlight);
	}

	/**
	 * Retrieves the low and high values by placing the robot on the
	 * BLACK foil and the SILVER tape respectively. Each step has to
	 * be confirmed by pressing a button.
	 *
	 * @see LightSensor#calibrateLow()
	 * @see LightSensor#calibrateHigh()
	 * @see LightSensor#getLow()
	 * @see LightSensor#getHigh()
	 */
	public void calibrate() {
		// calibrate low
		LCD.clearDisplay();
		LCD.drawString("BLACK", HSPACE, 1);
		LCD.drawString("-> button", HSPACE, 2);
		Sound.beep();
		Button.waitForPress();
		this.calibrateLow();
		this.black = this.getLow();

		// calibrate high
		LCD.clearDisplay();
		LCD.drawString("SILVER", HSPACE, 1);
		LCD.drawString("-> button", HSPACE, 2);
		Sound.beep();
		Button.waitForPress();
		this.calibrateHigh();
		this.silver = this.getHigh();

		// done
		LCD.clearDisplay();
		LCD.drawString("DONE", HSPACE, 1);
		LCD.drawString("-> button", HSPACE, 2);
		Sound.twoBeeps();
		Button.waitForPress();
		LCD.clearDisplay();
	}

	/**
	 * Stores the low and high value in 'calib.dat'. Attention:
	 * If 'calib.dat' already exists, it'll be deleted first.
	 */
	public void writeFile() {
		// stores the line index
		int line = 1;
	    File data = new File(FILE);
	    FileOutputStream out = null;
	    // delete file 'calib.dat' if it already exists
	    if(data.exists()) {
	    	data.delete();
	    }

	    // create file
	    try {
	    	data.createNewFile();
	    	out = new FileOutputStream(data);
	    } catch(IOException e) {
	    	LCD.clearDisplay();
	    	LCD.drawString("WRITE error", HSPACE, 1);
	    	LCD.drawString("-> can't create file", HSPACE, 2);
	    	Sound.buzz();
	    }

	    DataOutputStream dataOut = new DataOutputStream(out);
	    // write to stream
	    try {
	    	LCD.clearDisplay();
	    	LCD.drawString("WRITE started", HSPACE, line++);
	    	dataOut.writeInt(black);
	        LCD.drawString("Black val: " + black, HSPACE, line++);
	        dataOut.writeInt(silver);
	        LCD.drawString("Silvr val: " + silver, HSPACE, line++);
	        // flush the buffer and write the file
	        out.close();
	    } catch (IOException e) {
	    	LCD.clearDisplay();
	    	LCD.drawString("WRITE error", HSPACE, 1);
	    	LCD.drawString("-> can't write to file", HSPACE, 2);
	    	Sound.buzz();
	    }
	    LCD.drawString("WRITE done", HSPACE, line++);
	    LCD.drawString("-> button", HSPACE, line++);
	    Sound.twoBeeps();
	    Button.waitForPress();
	    LCD.clearDisplay();
	}

	/**
	 * Reads the low and high value from 'calib.dat'.
	 */
	public void readFile() {
		// stores the line index
		int line = 1;
		// stores the names of the integers in the file (in the right order)
		String[] names = {"Black val: ", "Silvr val: "};
		// counts the number of integers that were read from the file
		int count = 0;
	    File data = new File(FILE);

	    // read from file
	    try {
	    	FileInputStream is = new FileInputStream(data);
	    	DataInputStream dataIn = new DataInputStream(is);

    		LCD.clearDisplay();
    		LCD.drawString("READ started", HSPACE, line++);
	    	// at least 4 byte left to read (an int requires 4 byte)
    		while (is.available() > 3) {
	    		int val = dataIn.readInt();
	    		LCD.drawString(names[count] + val, HSPACE, line++);
	    		if(count == 0)
	    			this.setLow(val);
	    		else if(count == 1)
	    			this.setHigh(val);
	    		count++;
	    	}
	    	dataIn.close();
	    } catch (IOException e) {
	    	LCD.clearDisplay();
	    	LCD.drawString("READ error", HSPACE, 1);
	    	LCD.drawString("-> can't read from file", HSPACE, 2);
	    	Sound.buzz();
	    }
	    // check whether or not two integers were read successfully
	    if(count != 2) {
	    	// count != 2 e.g. when 'calib.dat' doesn't exist
	    	LCD.clearDisplay();
	    	LCD.drawString("READ error", HSPACE, 1);
	    	LCD.drawString("-> read != 2 integers", HSPACE, 2);
	    	Sound.buzz();
	    } else {
		    LCD.drawString("READ done", HSPACE, line++);
		    LCD.drawString("-> button", HSPACE, line++);
		    Sound.twoBeeps();
	    }
	    Button.waitForPress();
	    LCD.clearDisplay();
	}

	/**
	 * Starts the calibration process and verifies the result by printing
	 * the content of 'calib.dat'.
	 */
	public static void main(String[] args) {
		ACalibratedLS cls = new ACalibratedLS(SensorPort.S1, true);
		cls.calibrate();
		cls.writeFile();
		cls.readFile();
		LCD.drawString(" getLow(): ", HSPACE, 1);
		LCD.drawInt(cls.getLow(), HSPACE, 2);
		LCD.drawString("getHigh(): ", HSPACE, 3);
		LCD.drawInt(cls.getHigh(), HSPACE, 4);
		LCD.drawString("Testausgaben:", HSPACE, 5);
		Button.waitForPress();
		LCD.clearDisplay();
		while(true) {
			for(int i=0; i<8; i++) {
				LCD.drawInt(cls.readValue(), HSPACE, i);
			}
			Button.waitForPress();
		}

	}

}
